-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat!: add componentDidCatch for error handling #60
Conversation
@bchelkowski I heard that try/catch has a huge performance penalty and should be used for instance for third-party libs. I did not benchmark myself. Have you? What is the motivation to have such handler? When something breaks and app crashes its better to let the box crash rather to supress. At least in dev env. |
I will try to benchmark it somehow. Could you link some resources that say so? I want to review them.
The motivation is to be able to address better app crashes. So it does not answer the question should whether the app should crash or not, just gives the possibility to choose what should happen. |
I measured the most "fat" components in our application (using |
I think it was some discussion with Roku support. Have you reviewed https://developer.roku.com/en-gb/docs/references/brightscript/language/error-handling.md#proper-usage-of-exceptions?
It looks like it is optimized for no crash scenario.
|
Yes, I based on this documentation.
Yes, we can introduce bs_const in the app manifest (however the bs_const is mandatory so it will be a breaking change).
Good point, a crash is pointing to the rethrown line, which won't be a transparent solution. |
@bchelkowski Can this be turned off by default? I am wondering if we can have such handler per component meaning I can for instance have for a chosen component but not for all if bs_const feature flag is on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bchelkowski Wondering if we can inject #const to a file. But this would require each file modification via plugin?
src/components/renderer/Kopytko.brs
Outdated
sub _functionCall(func as Function, args = [] as Object, context = Invalid as Object) | ||
if (context = Invalid) then context = GetGlobalAA() | ||
|
||
argumentsNumber = args.count() | ||
context["$$functionCall"] = func | ||
|
||
if (argumentsNumber = 0) | ||
context["$$functionCall"]() | ||
else if (argumentsNumber = 1) | ||
context["$$functionCall"](args[0]) | ||
else if (argumentsNumber = 2) | ||
context["$$functionCall"](args[0], args[1]) | ||
end if | ||
|
||
context.delete("$$functionCall") | ||
end sub |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kopytko utils have function call function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, will add utils function
src/components/renderer/Kopytko.brs
Outdated
end try | ||
else | ||
_functionCall(func, args, context) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Less nesting if return is appended and no need for else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for a return
if there is else
. 😃
I can change to return. It doesn't matter to me.
src/components/renderer/Kopytko.brs
Outdated
@@ -2,6 +2,7 @@ sub init() | |||
m.state = {} | |||
m.elementToFocus = Invalid | |||
|
|||
m._enabledErrorHandling = Type(componentDidCatch) <> "<uninitialized>" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m._enabledErrorHandling = Type(componentDidCatch) <> "<uninitialized>" | |
m._enabledErrorCatching = Type(componentDidCatch) <> "<uninitialized>" |
What do you think?
Without bs_const I won't be able to catch original error (during development for example). Working with supressed or rethrown errors might be a nightmare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think?
I prefer Handling, but it doesn't matter to me so much, I can change to Catching.
Without bs_const I won't be able to catch original error (during development for example). Working with supressed or rethrown errors might be a nightmare
But if someone is suppressing the error for some reason, it shouldn't crash.
Some app components could always extend KopytkoGroup and have any logic the dev wants, it could be embedded there, so for example it could throw an error if they want.
And use this component instead of the KopytkoGroup.
For example, JavaScript suppressed error won't be thrown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, with the current solution, if you want to handle an error but have it thrown for the dev environment, you can create your component that will extend KopytkoGroup and use the MyAppGroup component instead of KopytkoGroup.
<?xml version="1.0" encoding="utf-8" ?>
<component name="MyAppGroup" extends="KopytkoGroup">
<script type="text/brightscript" uri="MyAppGroup.brs" />
</component>
sub componentDidCatch(error as Object, info as Object)
#if isDevelopmentEnvironment
?info
throw error
#end if
' ... normal production code
end sub
Of course, it won't be the ideal solution as the backtrace of the crash won't be full,
but you still have the original error message and the original line that crashed.
The app is stopped also in the component scope (8085 port), so maybe you are not exactly at the line of the function that crashed, but you can check the component context and variables.
It is a trade-off solution I won't argue with that, but it is also an optional one, I don't want to force anybody to use it.
Maybe I will have some idea in the future how it could be improved but, don't have it now.
I don't really follow. You mean some kind of new comment that will be converted to some kind of code value? |
e7f311a
to
8926608
Compare
So in Kopytko.brs file we can add a const with the value true/false and then depending on the value catch the function executions.
Something like that
Would have to be appended during build time. Also that means new flag would have to be needed for the config like |
Ok, but why actually? If someone doesn't define componentDidCatch for the component it won't use try/catch. |
Image the requirement. On prod env I want to report errors to some external service and degradate grafecully so I define |
|
135edbf
to
53f9bdb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good stuff
🎉 This PR is included in version 3.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
BREAKING CHANGE: Add
enableKopytkoComponentDidCatch
bs_const to the manifest fileWhat did you implement:
The new Lifecycle method
componentDidCatch
is invoked when the error is thrown within the component.How did you implement it:
componentDidCatch(error as Object, info as Object)
How can we verify it:
Create a component, add
componentDidCatch
method, and make the component throw an error.componentDidCatch
implementationexample of throwing an error
componentDidMount
implementationTodos:
Is this ready for review?: YES
Is it a breaking change?: YES